package com.wsy.sentinel;

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.spring.webflux.exception.SentinelBlockExceptionHandler;
import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;

/**
 * sentinel控流规则
 *
 * sentinel使用gateway全局过滤器filter对请求进行拦截,判断是否需要进行限流,熔断,热点操作
 *
 * sentinel工作原理:先对资源配置容错规则,请求到达服务器后,sentinel根据规则对请求进行容错
 * gateway工作原理:使用拦截器拦截请求,判断路由过滤器,转发请求
 */
@Component
public class FlowSentinelGatewayConfigure {

    //视图解析器
    private List<ViewResolver> viewResolvers;
    //服务编码配置
    private ServerCodecConfigurer serverCodecConfigurer;

    public FlowSentinelGatewayConfigure(ObjectProvider<List<ViewResolver>> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers =viewResolvers.getIfAvailable();
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    //初始化拦截器,拦截请求
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter(){
        return new SentinelGatewayFilter();
    }

    //sentinel控流规则声明  1.代码声明  2.注解声明
    //启用切面注解配置
//    @Bean
//    public SentinelResourceAspect sentinelResourceAspect(){
//        return new SentinelResourceAspect();
//    }

    /**
     * 限流规则
     */
    @PostConstruct
    public void setFlowRules(){
        //GatewayFlowRule:gateway限流规则
        //资源:gateway中的路由,资源名和路由id对应
        GatewayFlowRule flowRule = new GatewayFlowRule("user");
        flowRule.setGrade(1);  //阈值类型；1-->QPS
        flowRule.setCount(5);  //单机阈值
        flowRule.setIntervalSec(10); //窗口时间,默认单位秒

        GatewayFlowRule flowRule2 = new GatewayFlowRule("bookinfo");
        flowRule2.setGrade(1);  //阈值类型；1-->qbs
        flowRule2.setCount(5);  //单机阈值
        flowRule2.setIntervalSec(10); //窗口时间,默认单位秒

        //规则  不重复
        HashSet<GatewayFlowRule> rules = new HashSet<>();
        rules.add(flowRule);
        rules.add(flowRule2);

        //添加到规则中  规则管理器
        GatewayRuleManager.loadRules(rules);
    }

    /**
     * 异常处理器
     */
    //pre post 在构造方法执行完成之后,执行当前类操作
    @PostConstruct
    public SentinelBlockExceptionHandler sentinelBlockExceptionHandler(){
        return new SentinelBlockExceptionHandler(viewResolvers,serverCodecConfigurer);
    }

    //异常处理响应结果
    @PostConstruct
    public void initBlockRequestHandler(){
        //定义了处理器,处理发生了BlockException异常的请求
        BlockRequestHandler handler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                String msg = "{\"code\":\"500\",\"message\":\"接口限流\"}";
                //枚举类型 MediaType
                Mono<ServerResponse> responseMono = ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON_UTF8).bodyValue(msg);
                return responseMono;
            }
        };

        //设置使用处理器
        GatewayCallbackManager.setBlockHandler(handler);
    }

}
